﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Authentication;
using System.Windows.Forms;
using System.Xml.Serialization;
using BusinessLogic.BusinessEntity.Entities;
using BusinessLogic.Connectivity.Entities;
using BusinessLogic.Connectivity.Interfaces;
using BusinessLogic.Cryptography;
//using Microsoft.Office.Interop.Word;
using Microsoft.Office.Interop.Excel;
using UserInterface.ClientApplication.Exceptions;
using UserInterface.ClientApplication.UIEntity;
using System.Xml;
using System.Configuration;


namespace UserInterface.ClientApplication
{
	[Serializable]
	public class FormManager
	{
		private int GlobalID = 1;
		private BindingList<ClientView> Clients = new BindingList<ClientView>();
		private BindingList<AccountView> Accounts = new BindingList<AccountView>();
		private BindingList<RemittanceAdviceView> RemittanceAdvices = new BindingList<RemittanceAdviceView>();
		private BindingList<RemittanceAdviceView> SendedRA = new BindingList<RemittanceAdviceView>();
		public string MyName = "";
		public string MyUNP = "";

		[NonSerialized]
		private string FilePath = "data.dis";


		[NonSerialized]
		public string Login;


		[NonSerialized]
		public string Password;


		[NonSerialized]
		private BindingList<CurrencyView> Currencies = null;


		public Object Param { get; set; }

		public void SaveState()
		{
			BinaryFormatter formatter = new BinaryFormatter();
			using (FileStream fs = new FileStream(FilePath, FileMode.Create))
			{
				formatter.Serialize(fs, this);
			}
		}

		public void LoadState()
		{
			FormManager fm = new FormManager();
			BinaryFormatter formatter = new BinaryFormatter();
			using (FileStream fs = new FileStream(FilePath, FileMode.OpenOrCreate))
			{
				if (fs.Length == 0)
				{
					return;
				}
				fm = (FormManager)formatter.Deserialize(fs);
			}
			Accounts = fm.Accounts;
			Clients = fm.Clients;
			RemittanceAdvices = fm.RemittanceAdvices;
			SendedRA = fm.SendedRA;
			GlobalID = fm.GlobalID;
			MyName = fm.MyName;
			MyUNP = fm.MyUNP;
		}

		public void AddClient(ClientView c)
		{
			c.ID = GlobalID++;
			Clients.Add(c);
			SaveState();
		}

		public ClientView GetClient(int i)
		{
			if (i >= 0 && i < Clients.Count)
			{
				return Clients[i];
			}
			return null;
		}

		public BindingList<ClientView> GetAllClient()
		{
			return Clients;
		}

		public BindingList<ClientView> GetCopyClient()
		{
			return new BindingList<ClientView>(Clients);
		}

		public void RemoveClient(int i)
		{
			if (i >= 0 && i < Clients.Count)
			{
				Clients.RemoveAt(i);
			}
			SaveState();
		}

		public void RemoveClient(ClientView c)
		{
			Clients.Remove(c);
			SaveState();
		}

		public void AddAccount(AccountView a)
		{
			Accounts.Add(a);
			SaveState();
		}

		public AccountView GetAccount(int i)
		{
			if (i >= 0 && i < Accounts.Count)
			{
				return Accounts[i];
			}
			return null;
		}

		public BindingList<AccountView> GetAllAccount()
		{
			return Accounts;
		}

		public BindingList<AccountView> GetCopyAccount()
		{
			return new BindingList<AccountView>(Accounts);
		}


		public void RemoveAccount(int i)
		{
			if (i >= 0 && i < Accounts.Count)
			{
				Accounts.RemoveAt(i);
			}
			SaveState();
		}

		public void RemoveAccount(AccountView a)
		{
			Accounts.Remove(a);
			SaveState();
		}


		public void AddRA(RemittanceAdviceView r)
		{
			RemittanceAdvices.Add(r);
			SaveState();
		}


		public RemittanceAdviceView GetRA(int i)
		{
			if (i >= 0 && i < RemittanceAdvices.Count)
			{
				return RemittanceAdvices[i];
			}
			return null;
		}

		public BindingList<RemittanceAdviceView> GetAllRA()
		{
			return RemittanceAdvices;
		}

		public void RemoveRA(int i)
		{
			if (i >= 0 && i < RemittanceAdvices.Count)
			{
				RemittanceAdvices.RemoveAt(i);
				SaveState();
			}
		}

		public void RemoveRA(RemittanceAdviceView r)
		{
			RemittanceAdvices.Remove(r);
			SaveState();
		}



		public RemittanceAdviceView GetRAByEnvelopeID(int ID)
		{
			foreach (RemittanceAdviceView ra in SendedRA)
			{
				if (ra.DocumentEnvelopeID == ID)
				{
					return ra;
				}
			}
			return null;
		}


		public CurrencyView GetCurrency(int i)
		{
			if (Currencies == null)
			{
				LoadCurrency();
			}
			if (i >= 0 && i < Currencies.Count)
			{
				return Currencies[i];
			}
			return null;
		}

		private void LoadCurrency()
		{
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);
			var Envelopes = ServerProxy.Instance.GetCurrencies();
			if (Envelopes.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(Envelopes.Details);
			}
			Currencies = new BindingList<CurrencyView>();
			foreach (var cur in Envelopes.Payload)
			{
				Currencies.Add(new CurrencyView(cur));
			}
		}

		public BindingList<CurrencyView> GetAllCurrency()
		{
			if (Currencies == null)
			{
				LoadCurrency();
			}
			return Currencies;
		}




		public BindingList<AccountView> GetMyAccounts()
		{
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);
			var Envelopes = ServerProxy.Instance.GetClientAccounts(responce.Ticket);
			if (Envelopes.ServerResponseCode != ServerResponseCode.Success)
			{

				throw new ClientException(Envelopes.Details);
			}
			var result = new BindingList<AccountView>();
			AccountView accView = null;
			foreach (var acc in Envelopes.Payload)
			{
				accView = new AccountView(acc);
				accView.Currency.Name = GetCurrenciesByID(acc.Currency.ID).Name;
				result.Add(accView);
			}
			return result;
		}

		public BindingList<RemittanceAdviceView> GetSendedRA()
		{
			return SendedRA;
		}

		public void AddSendedRA(RemittanceAdviceView ra)
		{
			SendedRA.Add(ra);
			SaveState();

		}



		internal void PrintRemittanceAdvice(RemittanceAdviceView ra)
		{
			/*ApplicationClass wordApp = new ApplicationClass();
			object missing = System.Reflection.Missing.Value;
			object start = 0;
			object end = 0;
			wordApp.Visible = true;
			object readOnly = true;
			Object FileName = Directory.GetCurrentDirectory() + "\\RATemplate.docx";
			Document wordDoc = wordApp.Documents.Open(ref FileName, ref missing, ref readOnly, ref missing, ref missing,
													  ref missing, ref missing, ref missing, ref missing, ref missing,
													  ref missing, ref missing, ref missing, ref missing, ref missing,
													  ref missing);
			Replace(wordApp, "#1#", ra.Number);
			Replace(wordApp, "#2#", ra.IssueDate.ToString("dd MMMM yyyy"));
			Replace(wordApp, "#3#", ra.TotalAmount.ToString());
			Replace(wordApp, "#4#", ra.Currency.Name);
			Replace(wordApp, "#5#", ra.Description);
			Replace(wordApp, "#6#", ra.PayerPartyAccount.Accountee.Name);
			Replace(wordApp, "#7#", ra.BillToPartyAccount.Accountee.Name);


			wordApp.Visible = true;
			*/
			ApplicationClass excelApp = new ApplicationClass();
			Workbook activeWorkbook = excelApp.Workbooks.Open(Directory.GetCurrentDirectory() + "\\invoice.xls",
				0, false, 5, "", "", false, XlPlatform.xlWindows, "",
				true, false, 0, true, false, false);
			var sheet = (Worksheet)activeWorkbook.Worksheets.get_Item(1);
			sheet.Cells[3, 28] = ra.Number;
			sheet.Cells[3, 53] = ra.IssueDate.ToString("MM.dd.yyyy");
			sheet.Cells[6, 56] = ra.Currency.Code;
			sheet.Cells[4, 20] = RusCurrency.Str((double)ra.TotalAmount, ra.Currency.Name);

			sheet.Cells[23, 4] = MyUNP;
			sheet.Cells[7, 20] = MyName;
			if(ra.PayerPartyAccount != null)
				sheet.Cells[9, 57] = ra.PayerPartyAccount.Number;
			if(ra.BillToPartyAccount != null)
				sheet.Cells[16, 57] = ra.BillToPartyAccount.Number;
			if(ra.BillToPartyAccount != null && ra.BillToPartyAccount.Accountee != null)
			{
				sheet.Cells[14, 20] = ra.BillToPartyAccount.Accountee.Name;
				sheet.Cells[23, 24] = ra.BillToPartyAccount.Accountee.UNP;
			}
			sheet.Cells[17, 20] = ra.Description;
			sheet.Cells[6, 71] = ra.TotalAmount + " " + ra.Currency.Name;

			excelApp.Visible = true;
		}

		/*private void Replace(ApplicationClass word, string findTxt, string replacementText)
		{
			Range range = word.ActiveDocument.Content;


			object findtext = findTxt;
			object findreplacement = replacementText;
			object findforward = true;
			object findformat = false;
			object findwrap = WdFindWrap.wdFindContinue;
			object findmatchcase = false;
			object findmatchwholeword = false;
			object findmatchwildcards = false;
			object findmatchsoundslike = false;
			object findmatchallwordforms = false;
			object findreplace = WdReplace.wdReplaceAll;
			object nevim = false;

			range.Find.Execute(ref findtext, ref findmatchcase, ref findmatchwholeword, ref findmatchwildcards,
							   ref findmatchsoundslike, ref findmatchallwordforms, ref findforward, ref findwrap, ref findformat,
							   ref findreplacement, ref findreplace, ref nevim, ref nevim, ref nevim, ref nevim);
		}*/

		internal void ValidateUser(string login, string password)
		{
			Credentials credentials = new Credentials() { Login = login, Password = password };
			ServerResponse<User> response = ServerProxy.Instance.GetSessionTicket(credentials);

			if (response.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new AuthenticationException(response.Details);
			}
		}

		internal void UpdateRemittanceAdvice()
		{
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);

			if (responce.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(responce.Details);
			}
			var Envelopes = ServerProxy.Instance.GetSentEnvelopes(responce.Ticket);
			if (Envelopes.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(Envelopes.Details);
			}
			RemittanceAdviceView ra = null;
			for (int i = 0; i < Envelopes.Payload.Count; i++)
			{
				ra = GetRAByEnvelopeID(Envelopes.Payload[i].ID);
				if (ra != null)
				{
					ra.Status = Envelopes.Payload[i].Status.ToString();
					ra.RejectionReason = Envelopes.Payload[i].RejectionReason;
					if (ra.GetOriginalStatus() == "Processed")
					{
						//string s = Envelopes.Payload[i].DocXML;
						//s = s.Substring(s.IndexOf("<IssueDate>") + 11, 19);
						ra.IssueDate = Envelopes.Payload[i].SentDate;

					}
				}
			}
		}

		public static string GetSaveFileName(string dialogCaption)
		{
			OpenFileDialog saveDialog = new OpenFileDialog();

			saveDialog.RestoreDirectory = true;
			saveDialog.Title = dialogCaption;
			saveDialog.ShowDialog();

			if (saveDialog.FileName == String.Empty)
			{
				throw new ClientException("Нужно указать файл с ключом");
			}

			return saveDialog.FileName;
		}

		public bool SentRA(int ind)
		{
			RemittanceAdviceView RA = GetRA(ind);
			if (RA != null)
			{
				IServerContract proxy = ServerProxy.Instance;
				var resopnse = proxy.GetSessionTicket(new Credentials { Login = this.Login, Password = this.Password });
				string ticket = resopnse.Ticket;
				if (resopnse.ServerResponseCode != ServerResponseCode.Success)
				{
					throw new ClientException(resopnse.Details);
				}

				var fileName = GetSaveFileName("Выберите файл с ключом");
				var privateKeyXml = new XmlDocument();
				privateKeyXml.Load(fileName);

				string privateKey = privateKeyXml.DocumentElement.InnerText;

				RemittanceAdvice ra = RA.ToBE();

				var envelope = new DocumentEnvelope();
				envelope.DocumentType = DocumentType.RemittanceAdvice;
				envelope.DocXML = SerializeRAToXML(ra);
				envelope.Signature = RSADigitalSignatureManager.SignXmlFile(envelope.DocXML, privateKey);

				ServerResponse<DocumentEnvelope> response = proxy.ReceiveDocument(envelope, ticket);

				if (response.ServerResponseCode == ServerResponseCode.Success)
				{
					RA.Status = RemittanceAdviceStatus.Sent.ToString();

					//RemoveRA(RA);
					var copyRa = RA.Copy();
					RA.ID = 0;
					RA.Number = "";
					RA.Status = RemittanceAdviceStatus.Pending.ToString();
					copyRa.DocumentEnvelopeID = response.Payload.ID;
					AddSendedRA(copyRa);
					return true;
				}
				else
				{
					throw new ClientException(response.Details);
				}

			}
			return false;

		}


		private static string SerializeRAToXML(RemittanceAdvice advice)
		{
			using (var stringWriter = new StringWriter())
			{
				var serializer = new XmlSerializer(typeof(RemittanceAdvice));
				serializer.Serialize(stringWriter, advice);
				return stringWriter.GetStringBuilder().ToString();
			}
		}

		internal System.Collections.Generic.List<CurrencyConversion> GetCurrencyConversionBy()
		{
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);

			if (responce.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(responce.Details);
			}
			var Envelopes = ServerProxy.Instance.GetCurrencyConversions();
			if (Envelopes.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(Envelopes.Details);
			}
			var currencyConversions = Envelopes.Payload.ToList();
			foreach (var currConv in currencyConversions)
			{

				currConv.SourceCurrency.Name = GetCurrenciesByID(currConv.SourceCurrency.ID).Name;
				currConv.TargetCurrency.Name = GetCurrenciesByID(currConv.TargetCurrency.ID).Name;
			}
			return Envelopes.Payload.ToList();
		}

		private CurrencyView GetCurrenciesByID(int ID)
		{
			foreach (var cur in Currencies)
			{
				if (cur.ID == ID)
				{
					return cur;
				}
			}
			throw new ClientException("В курсах присутствует несуществующая валюта");

		}

		public bool IsExistAccount(AccountView accnt)
		{
			foreach (AccountView acc in Accounts)
			{
				if (!accnt.Equals(acc) && acc.Number == accnt.Number)
				{
					return true;
				}
			}
			return false;
		}

		public bool IsExistRA(RemittanceAdviceView RA)
		{
			foreach (RemittanceAdviceView ra in RemittanceAdvices)
			{
				if (!RA.Equals(ra) && ra.Number == RA.Number)
				{
					return true;
				}
			}
			foreach (RemittanceAdviceView ra in SendedRA)
			{
				if (!RA.Equals(ra) && ra.Number == RA.Number)
				{
					return true;
				}
			}
			return false;
		}

		internal void UpdateRA(RemittanceAdviceView RA, int ind)
		{
			var result = GetRA(ind);
			result.BillToPartyAccount = RA.BillToPartyAccount;
			result.Currency = RA.Currency;
			result.Description = RA.Description;
			result.DocumentEnvelopeID = RA.DocumentEnvelopeID;
			result.ID = RA.ID;
			result.Number = RA.Number;
			result.IssueDate = RA.IssueDate;
			result.PayerPartyAccount = RA.PayerPartyAccount;
			result.RejectionReason = RA.RejectionReason;
		}

		internal RemittanceAdviceView GetSendedRAByInd(int i)
		{
			if (i >= 0 && i < RemittanceAdvices.Count)
			{
				return SendedRA[i];
			}
			return null;
		}

		internal BindingList<RemittanceAdviceView> GetStatement(DateTime DateFrom, DateTime DateTo, AccountView account)
		{
			var result = new BindingList<RemittanceAdviceView>();
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);

			if (responce.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(responce.Details);
			}
			var receivedRA = ServerProxy.Instance.GetReceivedRemittanceAdvices(DateFrom, DateTo, responce.Ticket);
			var sentRA = ServerProxy.Instance.GetSentRemittanceAdvices(DateFrom, DateTo, responce.Ticket);
			var allAccount = ServerProxy.Instance.GetAllAccountsForUser(responce.Ticket);
			RemittanceAdviceView raView = new RemittanceAdviceView();
			foreach (var ra in receivedRA.Payload)
			{

				raView = new RemittanceAdviceView();
				raView.Number = ra.Number;
				raView.TotalAmount = ra.TotalAmount;
				raView.IssueDate = ra.IssueDate;
				raView.BillToPartyAccount = new AccountView();
				raView.BillToPartyAccount.Number = GetAccountByID(allAccount.Payload, ra.BillToPartyAccount.ID).Number;
				raView.PayerPartyAccount = new AccountView();
				raView.PayerPartyAccount.Number = GetAccountByID(allAccount.Payload, ra.PayerPartyAccount.ID).Number;
				raView.Currency = GetCurrenciesByID(ra.Currency.ID);
				if (raView.BillToPartyAccount.Number == account.Number)
				{
					result.Add(raView);
				}
			}

			foreach (var ra in sentRA.Payload)
			{

				raView = new RemittanceAdviceView();
				raView.Number = ra.Number;
				raView.TotalAmount = ra.TotalAmount;
				raView.IssueDate = ra.IssueDate;
				raView.BillToPartyAccount = new AccountView();
				raView.BillToPartyAccount.Number = GetAccountByID(allAccount.Payload, ra.BillToPartyAccount.ID).Number;
				raView.PayerPartyAccount = new AccountView();
				raView.PayerPartyAccount.Number = GetAccountByID(allAccount.Payload, ra.PayerPartyAccount.ID).Number;
				raView.Currency = GetCurrenciesByID(ra.Currency.ID);
				if (raView.PayerPartyAccount.Number == account.Number)
				{
					result.Add(raView);
				}
			}
			var resultList = result.OrderBy(r => r.IssueDate).ToList();
			result.Clear();
			foreach (var r in resultList)
			{
				result.Add(r);
			}
			return result;
		}

		internal decimal GetBalanceAfterPeriod(DateTime DateTo, AccountView account)
		{
			Credentials credentials = new Credentials();
			credentials.Login = Login;
			credentials.Password = Password;
			ServerResponse<User> responce = ServerProxy.Instance.GetSessionTicket(credentials);

			if (responce.ServerResponseCode != ServerResponseCode.Success)
			{
				throw new ClientException(responce.Details);
			}
			var receivedRA = ServerProxy.Instance.GetReceivedRemittanceAdvices(DateTo, DateTime.Now, responce.Ticket);
			var sentRA = ServerProxy.Instance.GetSentRemittanceAdvices(DateTo, DateTime.Now, responce.Ticket);
			var allAccount = ServerProxy.Instance.GetAllAccountsForUser(responce.Ticket);
			RemittanceAdviceView raView = new RemittanceAdviceView();
			decimal sum = 0;
			var myAcc = GetMyAccounts();
			foreach (var ra in receivedRA.Payload)
			{
				if (GetAccountByID(allAccount.Payload, ra.BillToPartyAccount.ID).Number == account.Number)
				{

					if (account.Currency.Name != ra.Currency.Name)
					{
						sum += ra.TotalAmount / GetRate(account.Currency, GetCurrenciesByID(ra.Currency.ID));
					}
					else
					{
						sum += ra.TotalAmount;
					}
				}
			}

			foreach (var ra in sentRA.Payload)
			{
				if (GetAccountByID(allAccount.Payload, ra.PayerPartyAccount.ID).Number == account.Number)
				{
					if (account.Currency.Name != ra.Currency.Name)
					{
						sum -= ra.TotalAmount / GetRate(account.Currency, GetCurrenciesByID(ra.Currency.ID));
					}
					else
					{
						sum -= ra.TotalAmount;
					}
				}
			}
			decimal sumNow = 0;
			sumNow += account.Balance;

			return sumNow - sum;
		}
		private Account GetAccountByID(IEnumerable<Account> list, int ID)
		{
			foreach (var l in list)
			{
				if (l.ID == ID)
				{
					return l;
				}
			}
			return new Account();
		}

		internal int GetMyAccountsByNumber(string number)
		{
			foreach (var acc in GetMyAccounts())
			{
				if (acc.Number == number)
				{
					return acc.ID;
				}
			}
			return -1;
		}





		internal decimal GetRate(CurrencyView sourceCurrency, CurrencyView destinationCurrency)
		{
			var currConv = GetCurrencyConversionBy();
			foreach(var cc in currConv)
			{
				if(cc.SourceCurrency.Name == sourceCurrency.Name && cc.TargetCurrency.Name == destinationCurrency.Name)
				{
					return cc.ConversionRate;
				}
			}
			return 1;
		}
	}
}